librery(broom)
Error in librery(broom) : no se pudo encontrar la función "librery"

1. Regresión Lineal Múltiple

1.1. Creación del modelo

Se carga un dataset de propiedades.

Éste cuenta con 8 variables:

  • id
  • l3 (barrio en el que se ubica la propiedad)
  • rooms (cantidad de habitaciones)
  • bathrooms (cantidad de baños)
  • surface_total (superficie total)
  • surface_covered (superficie cubierta)
  • price (precio)
  • property_type (tipo de propiedad)

Exceptuando la variable id que contiene valores únicos con el fin de identificar las propiedades, se crea un modelo de Regresión Lineal Múltiple para predecir el precio de cada propiedad a partir de las covariables restantes.

summary(modelo)

Call:
lm(formula = price ~ l3 + rooms + bathrooms + surface_total + 
    surface_covered + property_type, data = properties)

Residuals:
    Min      1Q  Median      3Q     Max 
-400904  -33817   -3307   24660  560915 

Coefficients:
                            Estimate Std. Error t value Pr(>|t|)    
(Intercept)               -109406.61    4788.67 -22.847  < 2e-16 ***
l3Agronomía                   623.53    8846.14   0.070 0.943807    
l3Almagro                   -4520.04    4295.24  -1.052 0.292650    
l3Balvanera                -24788.27    4551.65  -5.446 5.18e-08 ***
l3Barracas                 -10128.24    5351.06  -1.893 0.058397 .  
l3Barrio Norte              49921.81    4417.82  11.300  < 2e-16 ***
l3Belgrano                  69648.12    4283.55  16.259  < 2e-16 ***
l3Boca                     -47540.60    7076.20  -6.718 1.86e-11 ***
l3Boedo                    -19034.38    5219.54  -3.647 0.000266 ***
l3Caballito                  6220.15    4301.29   1.446 0.148153    
l3Catalinas                -76321.95   33563.74  -2.274 0.022974 *  
l3Centro / Microcentro     -29046.49    6781.80  -4.283 1.85e-05 ***
l3Chacarita                 11903.39    5299.02   2.246 0.024687 *  
l3Coghlan                   40820.55    5462.90   7.472 8.02e-14 ***
l3Colegiales                34073.02    4816.54   7.074 1.52e-12 ***
l3Congreso                 -32314.97    5494.75  -5.881 4.10e-09 ***
l3Constitución             -47292.98    6321.63  -7.481 7.50e-14 ***
l3Flores                   -22510.27    4536.15  -4.962 6.99e-07 ***
l3Floresta                 -28315.65    5069.38  -5.586 2.34e-08 ***
l3Las Cañitas               90455.90    5883.38  15.375  < 2e-16 ***
l3Liniers                  -20080.34    5366.27  -3.742 0.000183 ***
l3Mataderos                -33863.43    5424.79  -6.242 4.35e-10 ***
l3Monserrat                -32431.49    5228.46  -6.203 5.59e-10 ***
l3Monte Castro              -8770.72    5949.63  -1.474 0.140445    
l3Nuñez                     56958.42    4559.69  12.492  < 2e-16 ***
l3Once                     -30757.83    5456.51  -5.637 1.74e-08 ***
l3Palermo                   66169.58    4221.50  15.674  < 2e-16 ***
l3Parque Avellaneda        -34398.95    7598.09  -4.527 5.99e-06 ***
l3Parque Centenario        -12288.30    5016.45  -2.450 0.014305 *  
l3Parque Chacabuco         -22537.83    5314.36  -4.241 2.23e-05 ***
l3Parque Chas                5195.26    7542.97   0.689 0.490981    
l3Parque Patricios         -36808.02    5973.29  -6.162 7.24e-10 ***
l3Paternal                 -13314.50    5189.69  -2.566 0.010304 *  
l3Pompeya                  -79977.17    8035.74  -9.953  < 2e-16 ***
l3Puerto Madero            259015.83    5095.12  50.836  < 2e-16 ***
l3Recoleta                  64088.22    4360.34  14.698  < 2e-16 ***
l3Retiro                    26067.40    5281.27   4.936 8.01e-07 ***
l3Saavedra                  19492.00    4914.18   3.966 7.31e-05 ***
l3San Cristobal            -23739.75    4955.13  -4.791 1.67e-06 ***
l3San Nicolás              -26247.55    5168.96  -5.078 3.83e-07 ***
l3San Telmo                 -5653.85    4877.12  -1.159 0.246356    
l3Tribunales               -34608.17    8924.63  -3.878 0.000106 ***
l3Velez Sarsfield          -25943.69    8303.75  -3.124 0.001783 ** 
l3Versalles                -22232.13    6758.40  -3.290 0.001004 ** 
l3Villa Crespo               1595.26    4317.54   0.369 0.711770    
l3Villa del Parque          -3290.17    4866.59  -0.676 0.498997    
l3Villa Devoto              13301.39    4807.08   2.767 0.005659 ** 
l3Villa General Mitre      -19170.08    6802.25  -2.818 0.004831 ** 
l3Villa Lugano             -83039.18    6533.35 -12.710  < 2e-16 ***
l3Villa Luro                -7579.11    5404.78  -1.402 0.160833    
l3Villa Ortuzar             18667.61    6829.18   2.734 0.006269 ** 
l3Villa Pueyrredón          10516.80    5349.56   1.966 0.049314 *  
l3Villa Real                -8823.37    8745.56  -1.009 0.313030    
l3Villa Riachuelo          -32775.66   17171.10  -1.909 0.056298 .  
l3Villa Santa Rita          -5767.71    6383.86  -0.903 0.366274    
l3Villa Soldati           -136489.91   18944.29  -7.205 5.90e-13 ***
l3Villa Urquiza             30648.43    4418.91   6.936 4.09e-12 ***
rooms                       -3961.27     444.58  -8.910  < 2e-16 ***
bathrooms                   34040.98     644.28  52.836  < 2e-16 ***
surface_total                 919.08      23.52  39.069  < 2e-16 ***
surface_covered              1457.18      28.73  50.715  < 2e-16 ***
property_typeDepartamento   92653.32    2191.23  42.284  < 2e-16 ***
property_typePH             46779.37    2274.94  20.563  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 66580 on 45841 degrees of freedom
Multiple R-squared:  0.7764,    Adjusted R-squared:  0.7761 
F-statistic:  2568 on 62 and 45841 DF,  p-value: < 2.2e-16

1.2. Análisis del modelo

1.2.1. Interpretación de los coeficientes estimados

Según este modelo, -109406.61 es la ordenada al origen. Esto significa que, si encontrásemos una propiedad en la que todas las variables tuviesen un valor igual a 0, el modelo predeciría que dicha propiedad tendría un precio de -109406.61.

El resto de los coeficientes indica cómo varía, según la predicción de nuestro modelo, dicho precio inicial al aumentar en una unidad la variable en cuestión.

En el caso de las variables l3 y property_type, nos encontramos ante variables dummies, variales categóricas que indican que la propiedad tiene cierta cualidad o no. l3 se refiere al barrio donde se ubica la propiedad y property_type, al tipo de propiedad en que consiste (PH o departamento).

Dado que una propiedad no puede encontrarse en más de un lugar ni ser de más de un tipo, solo uno de los coeficientes para la variable l3 y uno para la variable property_type tendrá influencia en la predicción que realice nuestro modelo para cada propiedad. Por ejemplo, en el caso de un departamento de Caballito, nuestro modeo utilizará el coeficiente 6220.15 para la variable l3 y el coefciente 92653.32 para property_type. El resto de los coeficientes relacionados a l3 y a property_type no se utilizarán dado que la variable reflejará un valor igual a 0 en dichos casos, dejándolos anulados.

En particular, se observa que los barrios de Puerto Madero, Las Cañitas, Belgrano, Palermos, Recoleta, Núñez, Barrio Norte, Coghlan, Colegiales, Villa Urquiza, Retiro, Saavedra, Villa Ortúzar, Villa Devoto, Chacarita, Pueyrredń, Caballito, Parque Chas, Villa Crespo y Agronomía influirán positivamente en el precio estimado por el modelo puesto que sus coeficientes son positivos. El resto de los barrios, en cambio, presenta un coeficiente negativo por lo que el valor estimado presentará un decremento si la propiedad se encuentra ubicada en uno de ellos.

Las variables rooms, bathrooms, surface_total y surface_covered, por otro lado, son numéricas, y pueden tomar cualquier valor. La multiplicación de sus valores por sus respectivos coeficientes también tendrá un impacto en la predicción de nuestro modelo, ya sea generando un aumento en el precio, en el caso de los baños, la superficie total y la superficie cubierta, o ya sea provocando una disminución, en el caso de los dormitorios.

1.2.2. Significatividad de las variables dummies

Resulta interesante destacar que, a excepeción de los barrios de Agronomía, Almagro, Barracas, Caballito, Monte Castro, Parque Chas, San Telmo, Villa Crespo, Villa del Parque, Villa Luro, Villa Real y Santa Rita, los valores de la variable dummy l3 y property_type presentan un p-valor menor a 0.05, lo que significa que la variable es útil para explicar y cuando en el modelo se encuentran presentes las demás variables, pero no en otro caso.

Los coeficientes calculados para Agronomía y Villa Crespo, por su parte, muestran un valor mayor a 0.75, indicando que en estos casos la variable resulta significativa incluso en ausencia de las demás variables.

1.2.3. Evaluación del modelo

Respecto de la evaluación del modelo, podemos ver que este presenta un R² de 0.77, lo que indica que nuestro modelo es capaz de explicar el 77% de la variabilidad de la variable depediente y a partir de las variables independientes proporcionadas.

1.3. Predicciones

A continuación, se proponen dos nuevos casos y se utiliza el modelo ajustado para predecir sus precios.

El primer caso consiste en un departamento de 120 metros cuadrados cubiertos, ubicado en el barrio de Abasto, con 3 dormitorios y 2 baños. El segundo se trata de un PH en Balvanera, con 80 metros cuadrados cubiertos, 20 metros cuadrados no cubiertos, 2 dormitorios y 3 baños.

Se genera un nuevo dataset con estos casos y se utiliza la funció predict para predecir sus precios.

El modelo predice que la propiedad en el barrio de Abasto vale 324596.4, mientras que la del barrio de Balvanera cuesta 215267.6, por lo que resulta preferible tener la primera para vender.

1.4. Creación de un nuevo modelo

Se crea un nuevo modelo sin la variable l3.

summary(modelo_sin_l3)

Call:
lm(formula = price ~ rooms + bathrooms + surface_total + surface_covered + 
    property_type, data = properties)

Residuals:
    Min      1Q  Median      3Q     Max 
-518799  -36177   -9643   25740  724251 

Coefficients:
                            Estimate Std. Error t value Pr(>|t|)    
(Intercept)               -131096.86    2750.50  -47.66   <2e-16 ***
rooms                      -13348.53     519.02  -25.72   <2e-16 ***
bathrooms                   42664.68     756.37   56.41   <2e-16 ***
surface_total                 877.03      27.59   31.79   <2e-16 ***
surface_covered              1783.80      33.53   53.21   <2e-16 ***
property_typeDepartamento  135177.47    2513.93   53.77   <2e-16 ***
property_typePH             68598.52    2677.46   25.62   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 79210 on 45897 degrees of freedom
Multiple R-squared:  0.6832,    Adjusted R-squared:  0.6831 
F-statistic: 1.649e+04 on 6 and 45897 DF,  p-value: < 2.2e-16

Se observa que este modelo presenta una mayor cantidad de residuos. Si bien el número observado es menor (-9643) que en el modelo anterior (-3307), lo deseable es que la media y/o mediana de los residuos se acerque a 0.

Por otro lado, si bien los coeficientes estimados han variado su valor, no han cambiado su signo, por lo que su incidencia en la estimación de precios se mantiene: aquellas variables que incrementaban el valor de los precios continuan haciéndolo y lo mismo ocurre con las que disminuían este valor.

Finalmente, se puede que este segundo modelo posee una menor capacidad explicativa de la variable y. Su R² nos indica que las variables independientes solo pueden explicar el 68% de la variable dependiente. Este decremento podría resultar esperable en tanto este modelo utiliza menos variables. Los modelos de Regresión Lineal aumentan su R² conforme aumentan la cantidad de variables empleadas en la predicción, sin importar si estas están o no correlacionadas con la variable a predecir. Sin embargo, si observamos el R² ajustado, vemos que también aquí el primer modelo supera al segundo (77% contra 68%). Esto resulta relevante puesto que el R² ajustado indica la capacidad de explicación de un modelo considerando las variables independientes que sí se encuentran relacionadas con la dependiente. Esta medida disminuya al añadir variables no relacionadas y aumneta solo si las variables agregadas son efecitvamente explicativas. Debido a esto, el R² ajustado posibilita la comparación de modelos con distinta cantidad de variables y será el que utilizaremos para determinar qué modelo explica mejor la variable precios.

1.5. Comparación de modelos

El modelo que mejor explica la variable precios es el modelo que incluye la variable l3, dado que su R² ajustado. El modelo sin esta variable presenta una menor capacidad de explicación, solo el 68% de la variabilidad de la variable dependiente.

2. Creación de variables

2.1. Variable barrios

Grafico los precios de los barrios para evaluar si hay puntos de quiebre claros:

No lo hay. Se generan 3 bins y se establecen los puntos de corte a partir de ellos.

El de clusters no está tan bien balanceado. Uso el de contents.

2.2. Modelo con variable barrios

Se calcula un nuevo modelo.

summary(model_con_barrios)

Call:
lm(formula = price ~ rooms + bathrooms + surface_total + surface_covered + 
    property_type + barrios, data = properties_barrios)

Residuals:
    Min      1Q  Median      3Q     Max 
-457728  -36989   -5510   25167  701372 

Coefficients:
                            Estimate Std. Error t value Pr(>|t|)    
(Intercept)               -155886.83    2662.38  -58.55   <2e-16 ***
rooms                       -8868.58     481.81  -18.41   <2e-16 ***
bathrooms                   36265.52     702.19   51.65   <2e-16 ***
surface_total                 804.49      25.49   31.57   <2e-16 ***
surface_covered              1727.13      30.98   55.76   <2e-16 ***
property_typeDepartamento  113911.17    2333.81   48.81   <2e-16 ***
property_typePH             59415.69    2474.16   24.02   <2e-16 ***
barriosmedio                20501.46    1154.63   17.76   <2e-16 ***
barriosalto                 76571.89    1070.82   71.51   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 73130 on 45895 degrees of freedom
Multiple R-squared:   0.73, Adjusted R-squared:  0.7299 
F-statistic: 1.551e+04 on 8 and 45895 DF,  p-value: < 2.2e-16

Interpretación de los resultados.

Mejor modelo: con l3 por R cuadrado ajustado En mi opinión: el de barrios es más útil

2.3. Variable surface_patio

Chequeo si hay valores en sup total que sean menores a sup cubierta

properties_barrios[properties_barrios$surface_covered > properties_barrios$surface_total,]

No los hay. De haberlos habido, hubiese tomado como que no hay patio.

2.4. Modelo con la variable surface_patio

Se entrena un nuevo modelo

modelo_barriosYpatio <- lm(price ~ barrios + rooms + bathrooms + surface_covered + surface_patio +
                          property_type, data=properties_barriosYpatio)
summary(modelo_barriosYpatio)

Call:
lm(formula = price ~ barrios + rooms + bathrooms + surface_covered + 
    surface_patio + property_type, data = properties_barriosYpatio)

Residuals:
    Min      1Q  Median      3Q     Max 
-457728  -36989   -5510   25167  701372 

Coefficients:
                            Estimate Std. Error t value Pr(>|t|)    
(Intercept)               -155886.83    2662.38  -58.55   <2e-16 ***
barriosmedio                20501.46    1154.63   17.76   <2e-16 ***
barriosalto                 76571.89    1070.82   71.51   <2e-16 ***
rooms                       -8868.58     481.81  -18.41   <2e-16 ***
bathrooms                   36265.52     702.19   51.65   <2e-16 ***
surface_covered              2531.62      16.35  154.81   <2e-16 ***
surface_patio                 804.49      25.49   31.57   <2e-16 ***
property_typeDepartamento  113911.17    2333.81   48.81   <2e-16 ***
property_typePH             59415.69    2474.16   24.02   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 73130 on 45895 degrees of freedom
Multiple R-squared:   0.73, Adjusted R-squared:  0.7299 
F-statistic: 1.551e+04 on 8 and 45895 DF,  p-value: < 2.2e-16

surface_patio: mismo coeficiente que surface_total…

3. Evaluación de modelo

Analizar los residuos del modelo elaborado en 2.d

3.1. Modelo alternativo

Comparar la performance del modelo de 2.d con éste, tanto en términos de la variabilidad explicada cómo de su relación con los supuestos del modelo lineal. Re-interpretar los parámetros del modelo.

summary(model_log)

Call:
lm(formula = price ~ log(rooms) + log(bathrooms) + log(surface_covered) + 
    property_type + barrios + surface_patio, data = properties_barriosYpatio)

Residuals:
    Min      1Q  Median      3Q     Max 
-312712  -40705   -5552   30714  668163 

Coefficients:
                            Estimate Std. Error t value Pr(>|t|)    
(Intercept)               -813274.40    6144.76 -132.35   <2e-16 ***
log(rooms)                 -49447.72    1248.92  -39.59   <2e-16 ***
log(bathrooms)              64706.55    1260.88   51.32   <2e-16 ***
log(surface_covered)       222395.43    1467.16  151.58   <2e-16 ***
property_typeDepartamento   87666.94    2370.47   36.98   <2e-16 ***
property_typePH             30416.43    2523.98   12.05   <2e-16 ***
barriosmedio                20755.15    1189.84   17.44   <2e-16 ***
barriosalto                 76602.45    1103.09   69.44   <2e-16 ***
surface_patio                 703.80      26.32   26.74   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 75360 on 45895 degrees of freedom
Multiple R-squared:  0.7132,    Adjusted R-squared:  0.7132 
F-statistic: 1.427e+04 on 8 and 45895 DF,  p-value: < 2.2e-16

4. Modelos por tipo de propiedad

Anido el dataframe por la variable property_type.

Para cada tipo de propiedad, genero el modelo de 2d

summary(propTypes$model[[3]])

Call:
lm(formula = price ~ barrios + rooms + bathrooms + surface_covered + 
    surface_patio, data = df)

Residuals:
    Min      1Q  Median      3Q     Max 
-266658  -32757   -1815   29250  323310 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)     -3924.79    3308.05  -1.186    0.236    
barriosmedio    24976.69    2422.32  10.311   <2e-16 ***
barriosalto     74869.51    2406.89  31.106   <2e-16 ***
rooms             908.18    1114.47   0.815    0.415    
bathrooms       25088.75    1588.80  15.791   <2e-16 ***
surface_covered  1362.05      32.93  41.359   <2e-16 ***
surface_patio     423.80      36.49  11.615   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 56060 on 4361 degrees of freedom
Multiple R-squared:  0.6581,    Adjusted R-squared:  0.6576 
F-statistic:  1399 on 6 and 4361 DF,  p-value: < 2.2e-16

  1. El código fuente de este trabajo se encuentra disponible aquí.

LS0tCnRpdGxlOiAiVHJhYmFqbyBQcsOhY3RpY28gTsKwMl5bRWwgY8OzZGlnbyBmdWVudGUgZGUgZXN0ZSB0cmFiYWpvIHNlIGVuY3VlbnRyYSBkaXNwb25pYmxlICBbYXF1w61dKGh0dHBzOi8vZ2l0aHViLmNvbS9hbGFkYXNwYWxhYnJhcy9FRUEyMDE5L3RyZWUvbWFzdGVyL0VFQS1UUHMvVFAwMSkuXSIKYXV0aG9yOiAiTWFjYXJlbmEgRmVybmFuZGV6IFVycXVpemEiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBkZXB0aDogMwotLS0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CmRpdi5tYWluLWNvbnRhaW5lciB7CiAgbWF4LXdpZHRoOiAxNjAwcHg7CiAgbWFyZ2luLWxlZnQ6IGF1dG87CiAgbWFyZ2luLXJpZ2h0OiBhdXRvOwp9CmJvZHkgewp0ZXh0LWFsaWduOiBqdXN0aWZ5fQpoMXsKICBmb250LXNpemU6IDE5cHQKfQpoMnsKICBmb250LXNpemU6IDE3cHQKfQpoM3sKICBmb250LXNpemU6IDE1cHQKfQo8L3N0eWxlPgoKYGBge3IsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KE9uZVIpCmxpYnJhcnkoYnJvb20pCmBgYAoKIyAxLiBSZWdyZXNpw7NuIExpbmVhbCBNw7psdGlwbGUKCiMjIDEuMS4gQ3JlYWNpw7NuIGRlbCBtb2RlbG8KU2UgY2FyZ2EgdW4gZGF0YXNldCBkZSBwcm9waWVkYWRlcy4KCmBgYHtyfQpwcm9wZXJ0aWVzIDwtIHJlYWRfcmRzKCdhcl9wcm9wZXJ0aWVzLnJkcycpCmBgYAoKw4lzdGUgY3VlbnRhIGNvbiA4IHZhcmlhYmxlczoKCi0gX2lkXwotIF9sM18gKGJhcnJpbyBlbiBlbCBxdWUgc2UgdWJpY2EgbGEgcHJvcGllZGFkKQotIF9yb29tc18gIChjYW50aWRhZCBkZSBoYWJpdGFjaW9uZXMpCi0gX2JhdGhyb29tc18gKGNhbnRpZGFkIGRlIGJhw7FvcykKLSAqc3VyZmFjZV90b3RhbCogKHN1cGVyZmljaWUgdG90YWwpCi0gKnN1cmZhY2VfY292ZXJlZCogKHN1cGVyZmljaWUgY3ViaWVydGEpCi0gX3ByaWNlXyAocHJlY2lvKQotICpwcm9wZXJ0eV90eXBlKiAodGlwbyBkZSBwcm9waWVkYWQpCgpFeGNlcHR1YW5kbyBsYSB2YXJpYWJsZSBfaWRfIHF1ZSBjb250aWVuZSB2YWxvcmVzIMO6bmljb3MgY29uIGVsIGZpbiBkZSBpZGVudGlmaWNhciBsYXMgcHJvcGllZGFkZXMsCnNlIGNyZWEgdW4gbW9kZWxvIGRlIFJlZ3Jlc2nDs24gTGluZWFsIE3Dumx0aXBsZSBwYXJhIHByZWRlY2lyIGVsIHByZWNpbyBkZSBjYWRhIHByb3BpZWRhZCBhIHBhcnRpciBkZSBsYXMgY292YXJpYWJsZXMgcmVzdGFudGVzLgoKYGBge3J9Cm1vZGVsbyA8LSBsbShwcmljZSB+IGwzICsgcm9vbXMgKyBiYXRocm9vbXMgKyBzdXJmYWNlX3RvdGFsICsgc3VyZmFjZV9jb3ZlcmVkICsgcHJvcGVydHlfdHlwZSwKICAgICAgICAgICAgZGF0YT1wcm9wZXJ0aWVzKQoKbGVuZ3RoKHVuaXF1ZShwcm9wZXJ0aWVzJGwzKSkKc3VtbWFyeShtb2RlbG8pCmBgYAoKIyMgMS4yLiBBbsOhbGlzaXMgZGVsIG1vZGVsbwoKIyMjIDEuMi4xLiBJbnRlcnByZXRhY2nDs24gZGUgbG9zIGNvZWZpY2llbnRlcyBlc3RpbWFkb3MKClNlZ8O6biBlc3RlIG1vZGVsbywgLTEwOTQwNi42MSBlcyBsYSBvcmRlbmFkYSBhbCBvcmlnZW4uIEVzdG8gc2lnbmlmaWNhIHF1ZSwgc2kgZW5jb250csOhc2Vtb3MgdW5hIHByb3BpZWRhZCBlbiBsYSBxdWUgdG9kYXMgbGFzIHZhcmlhYmxlcyB0dXZpZXNlbiB1biB2YWxvciBpZ3VhbCBhIDAsIGVsIG1vZGVsbyBwcmVkZWNpcsOtYSBxdWUgZGljaGEgcHJvcGllZGFkIHRlbmRyw61hIHVuIHByZWNpbyBkZSAtMTA5NDA2LjYxLgoKRWwgcmVzdG8gZGUgbG9zIGNvZWZpY2llbnRlcyBpbmRpY2EgY8OzbW8gdmFyw61hLCBzZWfDum4gbGEgcHJlZGljY2nDs24gZGUgbnVlc3RybyBtb2RlbG8sIGRpY2hvIHByZWNpbyBpbmljaWFsIGFsIGF1bWVudGFyIGVuIHVuYSB1bmlkYWQgbGEgdmFyaWFibGUgZW4gY3Vlc3Rpw7NuLgoKRW4gZWwgY2FzbyBkZSBsYXMgdmFyaWFibGVzIF9sM18geSAqcHJvcGVydHlfdHlwZSosIG5vcyBlbmNvbnRyYW1vcyBhbnRlIHZhcmlhYmxlcyBkdW1taWVzLCB2YXJpYWxlcyBjYXRlZ8OzcmljYXMgcXVlIGluZGljYW4gcXVlIGxhIHByb3BpZWRhZCB0aWVuZSBjaWVydGEgY3VhbGlkYWQgbyBuby4gX2wzXyBzZSByZWZpZXJlIGFsIGJhcnJpbyBkb25kZSBzZSB1YmljYSBsYSBwcm9waWVkYWQgeSAqcHJvcGVydHlfdHlwZSosIGFsIHRpcG8gZGUgcHJvcGllZGFkIGVuIHF1ZSBjb25zaXN0ZSAoUEggbyBkZXBhcnRhbWVudG8pLgoKRGFkbyBxdWUgdW5hIHByb3BpZWRhZCBubyBwdWVkZSBlbmNvbnRyYXJzZSBlbiBtw6FzIGRlIHVuIGx1Z2FyIG5pIHNlciBkZSBtw6FzIGRlIHVuIHRpcG8sIHNvbG8gdW5vIGRlIGxvcyBjb2VmaWNpZW50ZXMgcGFyYSBsYSB2YXJpYWJsZSBfbDNfIHkgdW5vIHBhcmEgbGEgdmFyaWFibGUgKnByb3BlcnR5X3R5cGUqIHRlbmRyw6EgaW5mbHVlbmNpYSBlbiBsYSBwcmVkaWNjacOzbiBxdWUgcmVhbGljZSBudWVzdHJvIG1vZGVsbyBwYXJhIGNhZGEgcHJvcGllZGFkLiBQb3IgZWplbXBsbywgZW4gZWwgY2FzbyBkZSB1biBkZXBhcnRhbWVudG8gZGUgQ2FiYWxsaXRvLCBudWVzdHJvIG1vZGVvIHV0aWxpemFyw6EgZWwgY29lZmljaWVudGUgNjIyMC4xNSBwYXJhIGxhIHZhcmlhYmxlIF9sM18geSBlbCBjb2VmY2llbnRlIDkyNjUzLjMyIHBhcmEgKnByb3BlcnR5X3R5cGUqLiBFbCByZXN0byBkZSBsb3MgY29lZmljaWVudGVzIHJlbGFjaW9uYWRvcyBhIF9sM18geSBhICpwcm9wZXJ0eV90eXBlKiBubyBzZSB1dGlsaXphcsOhbiBkYWRvIHF1ZSBsYSB2YXJpYWJsZSByZWZsZWphcsOhIHVuIHZhbG9yIGlndWFsIGEgMCBlbiBkaWNob3MgY2Fzb3MsIGRlasOhbmRvbG9zIGFudWxhZG9zLiAKCkVuIHBhcnRpY3VsYXIsIHNlIG9ic2VydmEgcXVlIGxvcyBiYXJyaW9zIGRlIFB1ZXJ0byBNYWRlcm8sIExhcyBDYcOxaXRhcywgQmVsZ3Jhbm8sIFBhbGVybW9zLCBSZWNvbGV0YSwgTsO6w7FleiwgQmFycmlvIE5vcnRlLCBDb2dobGFuLCBDb2xlZ2lhbGVzLCBWaWxsYSBVcnF1aXphLCBSZXRpcm8sIFNhYXZlZHJhLCBWaWxsYSBPcnTDunphciwgVmlsbGEgRGV2b3RvLCBDaGFjYXJpdGEsIFB1ZXlycmVkxYQsIENhYmFsbGl0bywgUGFycXVlIENoYXMsIFZpbGxhIENyZXNwbyB5IEFncm9ub23DrWEgaW5mbHVpcsOhbiBwb3NpdGl2YW1lbnRlIGVuIGVsIHByZWNpbyBlc3RpbWFkbyBwb3IgZWwgbW9kZWxvIHB1ZXN0byBxdWUgc3VzIGNvZWZpY2llbnRlcyBzb24gcG9zaXRpdm9zLiBFbCByZXN0byBkZSBsb3MgYmFycmlvcywgZW4gY2FtYmlvLCBwcmVzZW50YSB1biBjb2VmaWNpZW50ZSBuZWdhdGl2byBwb3IgbG8gcXVlIGVsIHZhbG9yIGVzdGltYWRvIHByZXNlbnRhcsOhIHVuIGRlY3JlbWVudG8gc2kgbGEgcHJvcGllZGFkIHNlIGVuY3VlbnRyYSB1YmljYWRhIGVuIHVubyBkZSBlbGxvcy4KCkxhcyB2YXJpYWJsZXMgX3Jvb21zXywgX2JhdGhyb29tc18sICpzdXJmYWNlX3RvdGFsKiB5ICpzdXJmYWNlX2NvdmVyZWQqLCBwb3Igb3RybyBsYWRvLCBzb24gbnVtw6lyaWNhcywgeSBwdWVkZW4gdG9tYXIgY3VhbHF1aWVyIHZhbG9yLiBMYSBtdWx0aXBsaWNhY2nDs24gZGUgc3VzIHZhbG9yZXMgcG9yIHN1cyByZXNwZWN0aXZvcyBjb2VmaWNpZW50ZXMgdGFtYmnDqW4gdGVuZHLDoSB1biBpbXBhY3RvIGVuIGxhIHByZWRpY2Npw7NuIGRlIG51ZXN0cm8gbW9kZWxvLCB5YSBzZWEgZ2VuZXJhbmRvIHVuIGF1bWVudG8gZW4gZWwgcHJlY2lvLCBlbiBlbCBjYXNvIGRlIGxvcyBiYcOxb3MsIGxhIHN1cGVyZmljaWUgdG90YWwgeSBsYSBzdXBlcmZpY2llIGN1YmllcnRhLCBvIHlhIHNlYSBwcm92b2NhbmRvIHVuYSBkaXNtaW51Y2nDs24sIGVuIGVsIGNhc28gZGUgbG9zIGRvcm1pdG9yaW9zLgoKIyMjIDEuMi4yLiBTaWduaWZpY2F0aXZpZGFkIGRlIGxhcyB2YXJpYWJsZXMgZHVtbWllcwoKUmVzdWx0YSBpbnRlcmVzYW50ZSBkZXN0YWNhciBxdWUsIGEgZXhjZXBlY2nDs24gZGUgbG9zIGJhcnJpb3MgZGUgQWdyb25vbcOtYSwgQWxtYWdybywgQmFycmFjYXMsIENhYmFsbGl0bywgTW9udGUgQ2FzdHJvLCBQYXJxdWUgQ2hhcywgU2FuIFRlbG1vLCBWaWxsYSBDcmVzcG8sIFZpbGxhIGRlbCBQYXJxdWUsIFZpbGxhIEx1cm8sIFZpbGxhIFJlYWwgeSBTYW50YSBSaXRhLCBsb3MgdmFsb3JlcyBkZSBsYSB2YXJpYWJsZSBkdW1teSBfbDNfIHkgKnByb3BlcnR5X3R5cGUqIHByZXNlbnRhbiB1biAqcC12YWxvciogbWVub3IgYSAwLjA1LCBsbyBxdWUgc2lnbmlmaWNhIHF1ZSBsYSB2YXJpYWJsZSBlcyDDunRpbCBwYXJhIGV4cGxpY2FyIF95XyBjdWFuZG8gZW4gZWwgbW9kZWxvIHNlIGVuY3VlbnRyYW4gcHJlc2VudGVzIGxhcyBkZW3DoXMgdmFyaWFibGVzLCBwZXJvIG5vIGVuIG90cm8gY2Fzby4KCkxvcyBjb2VmaWNpZW50ZXMgY2FsY3VsYWRvcyBwYXJhIEFncm9ub23DrWEgeSBWaWxsYSBDcmVzcG8sIHBvciBzdSBwYXJ0ZSwgbXVlc3RyYW4gdW4gdmFsb3IgbWF5b3IgYSAwLjc1LCBpbmRpY2FuZG8gcXVlIGVuIGVzdG9zIGNhc29zIGxhIHZhcmlhYmxlIHJlc3VsdGEgc2lnbmlmaWNhdGl2YSBpbmNsdXNvIGVuIGF1c2VuY2lhIGRlIGxhcyBkZW3DoXMgdmFyaWFibGVzLgoKIyMjIDEuMi4zLiBFdmFsdWFjacOzbiBkZWwgbW9kZWxvCgpSZXNwZWN0byBkZSBsYSBldmFsdWFjacOzbiBkZWwgbW9kZWxvLCBwb2RlbW9zIHZlciBxdWUgZXN0ZSBwcmVzZW50YSB1biBSwrIgZGUgMC43NywgbG8gcXVlIGluZGljYSBxdWUgbnVlc3RybyBtb2RlbG8gZXMgY2FwYXogZGUgZXhwbGljYXIgZWwgNzclIGRlIGxhIHZhcmlhYmlsaWRhZCBkZSBsYSB2YXJpYWJsZSBkZXBlZGllbnRlIF95XyBhIHBhcnRpciBkZSBsYXMgdmFyaWFibGVzIGluZGVwZW5kaWVudGVzIHByb3BvcmNpb25hZGFzLgoKIyMgMS4zLiBQcmVkaWNjaW9uZXMKCkEgY29udGludWFjacOzbiwgc2UgcHJvcG9uZW4gZG9zIG51ZXZvcyBjYXNvcyB5IHNlIHV0aWxpemEgZWwgbW9kZWxvIGFqdXN0YWRvIHBhcmEgcHJlZGVjaXIgc3VzIHByZWNpb3MuCgpFbCBwcmltZXIgY2FzbyBjb25zaXN0ZSBlbiB1biBkZXBhcnRhbWVudG8gZGUgMTIwIG1ldHJvcyBjdWFkcmFkb3MgY3ViaWVydG9zLCB1YmljYWRvIGVuIGVsIGJhcnJpbyBkZSBBYmFzdG8sIGNvbiAzIGRvcm1pdG9yaW9zIHkgMiBiYcOxb3MuIEVsIHNlZ3VuZG8gc2UgdHJhdGEgZGUgdW4gUEggZW4gQmFsdmFuZXJhLCBjb24gODAgbWV0cm9zIGN1YWRyYWRvcyBjdWJpZXJ0b3MsIDIwIG1ldHJvcyBjdWFkcmFkb3Mgbm8gY3ViaWVydG9zLCAyIGRvcm1pdG9yaW9zIHkgMyBiYcOxb3MuCgpTZSBnZW5lcmEgdW4gbnVldm8gZGF0YXNldCBjb24gZXN0b3MgY2Fzb3MgeSBzZSB1dGlsaXphIGxhIGZ1bmNpw7MgX3ByZWRpY3RfIHBhcmEgcHJlZGVjaXIgc3VzIHByZWNpb3MuCgpgYGB7cn0KbnVldm9zX2Nhc29zIDwtIGRhdGEuZnJhbWUoCiAgbDMgPSBjKCdBYmFzdG8nLCAnQmFsdmFuZXJhJyksCiAgcm9vbXMgPSBjKDMsIDIpLAogIGJhdGhyb29tcyA9IGMoMiwgMyksCiAgc3VyZmFjZV90b3RhbCA9IGMoMTIwLCAxMDApLAogIHN1cmZhY2VfY292ZXJlZCA9IGMoMTIwLCA4MCksCiAgcHJvcGVydHlfdHlwZSA9IGMoJ0RlcGFydGFtZW50bycsICdQSCcpIAopCgpudWV2b3NfY2Fzb3MgJT4lIAogIG11dGF0ZShwcmVkaWN0ID0gcHJlZGljdChtb2RlbG8sIG5ld2RhdGEgPSAuKSkgJT4lIAogIHNlbGVjdChsMywgcHJlZGljdCwgZXZlcnl0aGluZygpKQpgYGAKCkVsIG1vZGVsbyBwcmVkaWNlIHF1ZSBsYSBwcm9waWVkYWQgZW4gZWwgYmFycmlvIGRlIEFiYXN0byB2YWxlIDMyNDU5Ni40LCBtaWVudHJhcyBxdWUgbGEgZGVsIGJhcnJpbyBkZSBCYWx2YW5lcmEgY3Vlc3RhIDIxNTI2Ny42LCBwb3IgbG8gcXVlIHJlc3VsdGEgcHJlZmVyaWJsZSB0ZW5lciBsYSBwcmltZXJhIHBhcmEgdmVuZGVyLgoKIyMgMS40LiBDcmVhY2nDs24gZGUgdW4gbnVldm8gbW9kZWxvCgpTZSBjcmVhIHVuIG51ZXZvIG1vZGVsbyBzaW4gbGEgdmFyaWFibGUgX2wzXy4KCmBgYHtyfQptb2RlbG9fc2luX2wzIDwtIGxtKHByaWNlIH4gcm9vbXMgKyBiYXRocm9vbXMgKyBzdXJmYWNlX3RvdGFsICsgc3VyZmFjZV9jb3ZlcmVkICsgcHJvcGVydHlfdHlwZSwKICAgICAgICAgICAgICAgICAgIGRhdGE9cHJvcGVydGllcykKCnN1bW1hcnkobW9kZWxvX3Npbl9sMykKYGBgCgpTZSBvYnNlcnZhIHF1ZSBlc3RlIG1vZGVsbyBwcmVzZW50YSB1bmEgbWF5b3IgY2FudGlkYWQgZGUgcmVzaWR1b3MuIFNpIGJpZW4gZWwgbsO6bWVybyBvYnNlcnZhZG8gZXMgbWVub3IgKC05NjQzKSBxdWUgZW4gZWwgbW9kZWxvIGFudGVyaW9yICgtMzMwNyksIGxvIGRlc2VhYmxlIGVzIHF1ZSBsYSBtZWRpYSB5L28gbWVkaWFuYSBkZSBsb3MgcmVzaWR1b3Mgc2UgYWNlcnF1ZSBhIDAuCgpQb3Igb3RybyBsYWRvLCBzaSBiaWVuIGxvcyBjb2VmaWNpZW50ZXMgZXN0aW1hZG9zIGhhbiB2YXJpYWRvIHN1IHZhbG9yLCBubyBoYW4gY2FtYmlhZG8gc3Ugc2lnbm8sIHBvciBsbyBxdWUgc3UgaW5jaWRlbmNpYSBlbiBsYSBlc3RpbWFjacOzbiBkZSBwcmVjaW9zIHNlIG1hbnRpZW5lOiBhcXVlbGxhcyB2YXJpYWJsZXMgcXVlIGluY3JlbWVudGFiYW4gZWwgdmFsb3IgZGUgbG9zIHByZWNpb3MgY29udGludWFuIGhhY2nDqW5kb2xvIHkgbG8gbWlzbW8gb2N1cnJlIGNvbiBsYXMgcXVlIGRpc21pbnXDrWFuIGVzdGUgdmFsb3IuCgpGaW5hbG1lbnRlLCBzZSBwdWVkZSBxdWUgZXN0ZSBzZWd1bmRvIG1vZGVsbyBwb3NlZSB1bmEgbWVub3IgY2FwYWNpZGFkIGV4cGxpY2F0aXZhIGRlIGxhIHZhcmlhYmxlIF95Xy4gU3UgUsKyIG5vcyBpbmRpY2EgcXVlIGxhcyB2YXJpYWJsZXMgaW5kZXBlbmRpZW50ZXMgc29sbyBwdWVkZW4gZXhwbGljYXIgZWwgNjglIGRlIGxhIHZhcmlhYmxlIGRlcGVuZGllbnRlLiBFc3RlIGRlY3JlbWVudG8gcG9kcsOtYSByZXN1bHRhciBlc3BlcmFibGUgZW4gdGFudG8gZXN0ZSBtb2RlbG8gdXRpbGl6YSBtZW5vcyB2YXJpYWJsZXMuIExvcyBtb2RlbG9zIGRlIFJlZ3Jlc2nDs24gTGluZWFsIGF1bWVudGFuIHN1IFLCsiBjb25mb3JtZSBhdW1lbnRhbiBsYSBjYW50aWRhZCBkZSB2YXJpYWJsZXMgZW1wbGVhZGFzIGVuIGxhIHByZWRpY2Npw7NuLCBzaW4gaW1wb3J0YXIgc2kgZXN0YXMgZXN0w6FuIG8gbm8gY29ycmVsYWNpb25hZGFzIGNvbiBsYSB2YXJpYWJsZSBhIHByZWRlY2lyLiBTaW4gZW1iYXJnbywgc2kgb2JzZXJ2YW1vcyBlbCBSwrIgYWp1c3RhZG8sIHZlbW9zIHF1ZSB0YW1iacOpbiBhcXXDrSBlbCBwcmltZXIgbW9kZWxvIHN1cGVyYSBhbCBzZWd1bmRvICg3NyUgY29udHJhIDY4JSkuIEVzdG8gcmVzdWx0YSByZWxldmFudGUgcHVlc3RvIHF1ZSBlbCBSwrIgYWp1c3RhZG8gaW5kaWNhIGxhIGNhcGFjaWRhZCBkZSBleHBsaWNhY2nDs24gZGUgdW4gbW9kZWxvIGNvbnNpZGVyYW5kbyBsYXMgdmFyaWFibGVzIGluZGVwZW5kaWVudGVzIHF1ZSBzw60gc2UgZW5jdWVudHJhbiByZWxhY2lvbmFkYXMgY29uIGxhIGRlcGVuZGllbnRlLiBFc3RhIG1lZGlkYSBkaXNtaW51eWEgYWwgYcOxYWRpciB2YXJpYWJsZXMgbm8gcmVsYWNpb25hZGFzIHkgYXVtbmV0YSBzb2xvIHNpIGxhcyB2YXJpYWJsZXMgYWdyZWdhZGFzIHNvbiBlZmVjaXR2YW1lbnRlIGV4cGxpY2F0aXZhcy4gRGViaWRvIGEgZXN0bywgZWwgUsKyIGFqdXN0YWRvIHBvc2liaWxpdGEgbGEgY29tcGFyYWNpw7NuIGRlIG1vZGVsb3MgY29uIGRpc3RpbnRhIGNhbnRpZGFkIGRlIHZhcmlhYmxlcyB5IHNlcsOhIGVsIHF1ZSB1dGlsaXphcmVtb3MgcGFyYSBkZXRlcm1pbmFyIHF1w6kgbW9kZWxvIGV4cGxpY2EgbWVqb3IgbGEgdmFyaWFibGUgcHJlY2lvcy4KCiMjIDEuNS4gQ29tcGFyYWNpw7NuIGRlIG1vZGVsb3MKCkVsIG1vZGVsbyBxdWUgbWVqb3IgZXhwbGljYSBsYSB2YXJpYWJsZSBwcmVjaW9zIGVzIGVsIG1vZGVsbyBxdWUgaW5jbHV5ZSBsYSB2YXJpYWJsZSBfbDNfLCBkYWRvIHF1ZSBzdSBSwrIgYWp1c3RhZG8uIEVsIG1vZGVsbyBzaW4gZXN0YSB2YXJpYWJsZSBwcmVzZW50YSB1bmEgbWVub3IgY2FwYWNpZGFkIGRlIGV4cGxpY2FjacOzbiwgc29sbyBlbCA2OCUgZGUgbGEgdmFyaWFiaWxpZGFkIGRlIGxhIHZhcmlhYmxlIGRlcGVuZGllbnRlLgoKIyAyLiBDcmVhY2nDs24gZGUgdmFyaWFibGVzCgojIyAyLjEuIFZhcmlhYmxlIF9iYXJyaW9zXwoKR3JhZmljbyBsb3MgcHJlY2lvcyBkZSBsb3MgYmFycmlvcyBwYXJhIGV2YWx1YXIgc2kgaGF5IHB1bnRvcyBkZSBxdWllYnJlIGNsYXJvczoKCmBgYHtyfQpwbG90KHNvcnQocHJvcGVydGllcyRwcmljZSwgZGVjcmVhc2luZyA9IEZBTFNFKSwKICAgICBtYWluICA9ICdQcmVjaW9zIG9yZGVuYWRvcyBlbiBmb3JtYSBjcmVjaWVudGUnLAogICAgIHlsYWIgPSAnUHJlY2lvcycsCiAgICAgeGxhYiA9ICfDjW5kaWNlJywKICAgICBjb2wgPSAnY3lhbjQnKQpgYGAKCk5vIGxvIGhheS4gU2UgZ2VuZXJhbiAzIGJpbnMgeSBzZSBlc3RhYmxlY2VuIGxvcyBwdW50b3MgZGUgY29ydGUgYSBwYXJ0aXIgZGUgZWxsb3MuCgpgYGB7cn0KcHJlY2lvcyA8LSBwcm9wZXJ0aWVzICU+JSAKICBzZWxlY3QobDMsIHByaWNlKSAlPiUgCiAgZ3JvdXBfYnkobDMpICU+JSAKICBzdW1tYXJpc2UobWVkaWFfcHJlY2lvID0gbWVhbihwcmljZSkpICU+JSAKICBhcnJhbmdlKG1lZGlhX3ByZWNpbykKCnByZWNpb3MKYGBgCgoKYGBge3J9CnByZWNpb3MkYmlucyA8LSBiaW4ocHJlY2lvcyRtZWRpYV9wcmVjaW8sIAogICAgICAgICAgICAgICAgICAgIG5iaW5zPTMsIAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJiYWpvIiwgIm1lZGlvIiwgImFsdG8iKSwgCiAgICAgICAgICAgICAgICAgICAgbWV0aG9kPSJjb250ZW50IikKCnByZWNpb3MkY2x1c3RlcnMgPC0gYmluKHByZWNpb3MkbWVkaWFfcHJlY2lvLAogICAgICAgICAgICAgICAgICAgICAgICBuYmlucz0zLCAKICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoImJham8iLCAibWVkaW8iLCAiYWx0byIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kPSJjbHVzdGVycyIpCgpwcmVjaW9zCmBgYAoKYGBge3J9CnByZWNpb3MgJT4lIAogIGNvdW50KGJpbnMpCgpwcmVjaW9zICU+JSAKICBjb3VudChjbHVzdGVycykKYGBgCkVsIGRlIGNsdXN0ZXJzIG5vIGVzdMOhIHRhbiBiaWVuIGJhbGFuY2VhZG8uIFVzbyBlbCBkZSBjb250ZW50cy4KCmBgYHtyfQpwcm9wZXJ0aWVzX2JhcnJpb3MgPC0gbGVmdF9qb2luKHByb3BlcnRpZXMsIHByZWNpb3NbLGMoJ2wzJywgJ2JpbnMnKV0sIGJ5PSdsMycpCm5hbWVzKHByb3BlcnRpZXNfYmFycmlvcylbbmFtZXMocHJvcGVydGllc19iYXJyaW9zKSA9PSAnYmlucyddIDwtICdiYXJyaW9zJwoKcHJvcGVydGllc19iYXJyaW9zCmBgYAoKIyMgMi4yLiBNb2RlbG8gY29uIHZhcmlhYmxlIF9iYXJyaW9zXwoKU2UgY2FsY3VsYSB1biBudWV2byBtb2RlbG8uCgpgYGB7cn0KbW9kZWxfY29uX2JhcnJpb3MgPC0gbG0ocHJpY2UgfiByb29tcyArIGJhdGhyb29tcyArIHN1cmZhY2VfdG90YWwgKyBzdXJmYWNlX2NvdmVyZWQgKwogICAgICAgICAgICAgICAgICAgICAgICAgIHByb3BlcnR5X3R5cGUgKyBiYXJyaW9zLCBkYXRhPXByb3BlcnRpZXNfYmFycmlvcykKCnN1bW1hcnkobW9kZWxfY29uX2JhcnJpb3MpCmBgYApJbnRlcnByZXRhY2nDs24gZGUgbG9zIHJlc3VsdGFkb3MuCgpNZWpvciBtb2RlbG86IGNvbiBsMyBwb3IgUiBjdWFkcmFkbyBhanVzdGFkbwpFbiBtaSBvcGluacOzbjogZWwgZGUgYmFycmlvcyBlcyBtw6FzIMO6dGlsCgojIyAyLjMuIFZhcmlhYmxlICpzdXJmYWNlX3BhdGlvKgoKQ2hlcXVlbyBzaSBoYXkgdmFsb3JlcyBlbiBzdXAgdG90YWwgcXVlIHNlYW4gbWVub3JlcyBhIHN1cCBjdWJpZXJ0YQoKYGBge3J9CnByb3BlcnRpZXNfYmFycmlvc1twcm9wZXJ0aWVzX2JhcnJpb3Mkc3VyZmFjZV9jb3ZlcmVkID4gcHJvcGVydGllc19iYXJyaW9zJHN1cmZhY2VfdG90YWwsXQpgYGAKCk5vIGxvcyBoYXkuIERlIGhhYmVybG9zIGhhYmlkbywgaHViaWVzZSB0b21hZG8gY29tbyBxdWUgbm8gaGF5IHBhdGlvLgoKYGBge3J9CnByb3BlcnRpZXNfYmFycmlvc1lwYXRpbyA8LSBwcm9wZXJ0aWVzX2JhcnJpb3MgJT4lIAogIG11dGF0ZShzdXJmYWNlX3BhdGlvID0gc3VyZmFjZV90b3RhbC1zdXJmYWNlX2NvdmVyZWQpCgpwcm9wZXJ0aWVzX2JhcnJpb3NZcGF0aW9bcHJvcGVydGllc19iYXJyaW9zWXBhdGlvJHN1cmZhY2VfcGF0aW88MCxdCgpwcm9wZXJ0aWVzX2JhcnJpb3NZcGF0aW8KYGBgCgojIyAyLjQuIE1vZGVsbyBjb24gbGEgdmFyaWFibGUgKnN1cmZhY2VfcGF0aW8qCgpTZSBlbnRyZW5hIHVuIG51ZXZvIG1vZGVsbwoKYGBge3J9Cm1vZGVsb19iYXJyaW9zWXBhdGlvIDwtIGxtKHByaWNlIH4gYmFycmlvcyArIHJvb21zICsgYmF0aHJvb21zICsgc3VyZmFjZV9jb3ZlcmVkICsgc3VyZmFjZV9wYXRpbyArCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvcGVydHlfdHlwZSwgZGF0YT1wcm9wZXJ0aWVzX2JhcnJpb3NZcGF0aW8pCgpzdW1tYXJ5KG1vZGVsb19iYXJyaW9zWXBhdGlvKQpgYGAKc3VyZmFjZV9wYXRpbzogbWlzbW8gY29lZmljaWVudGUgcXVlIHN1cmZhY2VfdG90YWwuLi4KCiMgMy4gRXZhbHVhY2nDs24gZGUgbW9kZWxvCgpBbmFsaXphciBsb3MgcmVzaWR1b3MgZGVsIG1vZGVsbyBlbGFib3JhZG8gZW4gMi5kCgojIyAzLjEuIE1vZGVsbyBhbHRlcm5hdGl2bwoKQ29tcGFyYXIgbGEgcGVyZm9ybWFuY2UgZGVsIG1vZGVsbyBkZSAyLmQgY29uIMOpc3RlLCB0YW50byBlbiB0w6lybWlub3MgZGUgbGEgdmFyaWFiaWxpZGFkIGV4cGxpY2FkYSBjw7NtbyBkZSBzdSByZWxhY2nDs24gY29uIGxvcyBzdXB1ZXN0b3MgZGVsIG1vZGVsbyBsaW5lYWwuIFJlLWludGVycHJldGFyIGxvcyBwYXLDoW1ldHJvcyBkZWwgbW9kZWxvLgoKYGBge3J9Cm1vZGVsX2xvZyA9IGxtKHByaWNlIH4gbG9nKHJvb21zKSArIGxvZyhiYXRocm9vbXMpICsgbG9nKHN1cmZhY2VfY292ZXJlZCkgKyBwcm9wZXJ0eV90eXBlICsgCiAgICAgICAgICAgICAgICAgYmFycmlvcyArIHN1cmZhY2VfcGF0aW8sIGRhdGE9cHJvcGVydGllc19iYXJyaW9zWXBhdGlvKQoKc3VtbWFyeShtb2RlbF9sb2cpCmBgYAoKIyA0LiBNb2RlbG9zIHBvciB0aXBvIGRlIHByb3BpZWRhZAoKQW5pZG8gZWwgZGF0YWZyYW1lIHBvciBsYSB2YXJpYWJsZSAqcHJvcGVydHlfdHlwZSouCgpgYGB7cn0KcHJvcFR5cGVzIDwtIHByb3BlcnRpZXNfYmFycmlvc1lwYXRpbyAlPiUgCiAgZ3JvdXBfYnkocHJvcGVydHlfdHlwZSkgJT4lIAogIG5lc3QoKQoKcHJvcFR5cGVzCmBgYAoKUGFyYSBjYWRhIHRpcG8gZGUgcHJvcGllZGFkLCBnZW5lcm8gZWwgbW9kZWxvIGRlIDJkCgpgYGB7cn0KcGF0aW9fbW9kZWwgPC0gZnVuY3Rpb24oZGYpIHsKICBsbShwcmljZSB+IGJhcnJpb3MgKyByb29tcyArIGJhdGhyb29tcyArIHN1cmZhY2VfY292ZXJlZCArIHN1cmZhY2VfcGF0aW8sIGRhdGE9ZGYpCn0KCnByb3BUeXBlcyA8LSBwcm9wVHlwZXMgJT4lIAogIG11dGF0ZShtb2RlbCA9IG1hcChkYXRhLCBwYXRpb19tb2RlbCkpCgpwcm9wVHlwZXMKYGBgCgpgYGB7cn0KZ2V0X2NvZWZmaWNpZW50cyA8LSBmdW5jdGlvbihtb2QpewogIGMgPC0gY29lZihtb2QpICU+JSAKICAgIGFzLmxpc3QoKSAlPiUgCiAgICBhc190aWJibGUoKQogIHJldHVybihjKQp9Cgpwcm9wVHlwZXMgJT4lIAogIG11dGF0ZShjb2VmZiA9IG1hcChtb2RlbCwgZ2V0X2NvZWZmaWNpZW50cyksCiAgICAgICAgIGdsbmMgPSBtYXAobW9kZWwsIGdsYW5jZSkpICU+JSAKICB1bm5lc3QoY29lZmYsIGdsbmMsIC5kcm9wID0gVFJVRSkKYGBgCgpgYGB7cn0Kc3VtbWFyeShwcm9wVHlwZXMkbW9kZWxbWzFdXSkKc3VtbWFyeShwcm9wVHlwZXMkbW9kZWxbWzJdXSkKc3VtbWFyeShwcm9wVHlwZXMkbW9kZWxbWzNdXSkKYGBgCgo=